home *** CD-ROM | disk | FTP | other *** search
/ Adobe Graphics & Publishing SDK 1996 December / Adobe Graphics & Publishing SDK 1996 December.iso / mac / Premiere 4.2 SDK r3 Mac / Examples / Projects / Storyboard / Storyboard.c < prev    next >
Text File  |  1996-01-25  |  31KB  |  887 lines

  1. //========================================================================================
  2. //
  3. // Storyboard.c - Export marked frames as one or more storyboard PICT files.
  4. //
  5. // Written by Randy Ubillos and Bryan K. ╥Beaker╙ Ressler.
  6. //
  7. // Copyright ⌐ 1993-96, Adobe Systems Incorporated, all rights reserved worldwide.
  8. //
  9. // Version    1.00    10/20/93    Original version.
  10. // Version    1.01    9/12/94        Updated for 4.0.
  11. // Version  1.02    10/8/95     Updated for 4.2 and CW7.
  12. //
  13. //========================================================================================
  14.  
  15. //========================================================================================
  16. // Includes - use precompiled headers if compiling with CodeWarrior.
  17. //========================================================================================
  18. #ifdef __MWERKS__
  19.     #ifdef powerc
  20.         #include "PremierePPC"
  21.     #else
  22.         #include "Premiere68k"
  23.     #endif
  24. #else
  25.     #include "Premiere.h"
  26. #endif
  27.  
  28. //========================================================================================
  29. // Types
  30. //========================================================================================
  31. typedef struct {
  32.     short        hSize;            // Horizontal image size in pixels
  33.     short        vSize;            // Vertical image size in pixels
  34.     short        rows;            // Number of rows vertically
  35.     short        columns;        // Number of columns horizontally
  36.     short        spacing;        // Spacing in pixels
  37. } SBSettingsRec, *SBSettingsPtr, **SBSettingsHdl;
  38.  
  39. typedef struct {
  40.     SBSettingsRec    settings;    // Our current settings
  41.     Rect            vidRect;    // The frame rectangle
  42.     GWorldPtr        offWorld;    // Offworld only allocated during settings dialog
  43.     short            ref;        // The reference number of the file we're writing to
  44.     short            error;        // The last error code, if non-zero
  45.     PicHandle        thePic;        // The picture we're currently collecting
  46.     long            count;        // The running byte count for this picture
  47.     long            writeUsed;    // The number of bytes used in the write buffer
  48.     Ptr                writeBuf;    // The write buffer itself
  49. } LocalRec;
  50.  
  51. //========================================================================================
  52. // Constants
  53. //========================================================================================
  54. enum {                        // Resource IDs
  55.     srStrings = 2000,            // STR# - general string list
  56.     srDialog = 2000,            // DLOG - settings dialog
  57.     srExtension = 2100,            // TEXT - expansion template for numbered file extension
  58.     srGray = 2000                // PAT  - 50% gray pattern
  59. };
  60.  
  61. enum {                        // Items in our settings dialog
  62.     iOK = 1,
  63.     iCancel,
  64.     iLoad,
  65.     iSave,
  66.     iHorizSize,                    // 5
  67.     iVertSize,
  68.     iRowSlider,
  69.     iRowNumber,
  70.     iColumnSlider,
  71.     iColumnNumber,                // 10
  72.     iSpacingSlider,
  73.     iSpacingNumber,
  74.     iExample,
  75.     iImageSizeTitle,
  76.     iImageSizeGroup                // 15
  77. };
  78.  
  79. enum {                        // Items in our string list
  80.     ssPICTPrompt = 1,            // Prompt for PICT file name
  81.     ssPICTDefaultName,            // Default name for PICT file
  82.     ssSetPrompt,                // Prompt for settings file name
  83.     ssSetDefaultName,            // Default name for settings file
  84.     ssError                        // An error occurred
  85. };
  86.  
  87. enum {                        // Other constants
  88.     kSBSetType = 'SBsá',        // File type of the storyboard settings file
  89.     kWriteBufSize = 32768,        // The size of our write-ahead buffer
  90.     kFlushBufCmd = -1,            // The magic byteSize that says to flush the buffer
  91.     kRedraw = true,                // For SetToDialog
  92.     kNoRedraw = false,
  93.     kMinHSize = 100,            // Min/max output picture sizes
  94.     kMaxHSize = 4000,
  95.     kMinVSize = 100,
  96.     kMaxVSize = 4000
  97. };
  98.  
  99. //========================================================================================
  100. // Static prototypes
  101. //========================================================================================
  102. static void FileNameForPage(StringPtr inName, StringPtr outName, short which);
  103. static OSErr WriteZeros(short ref, long numBytes);
  104. static pascal void PutPICTData(char *buffer, short byteCount);
  105. static void InitSettingsRec(SBSettingsPtr settings);
  106. static void SetToDialog(DialogPtr settingsDialog, SBSettingsPtr set, Boolean redraw);
  107. static void DialogToSet(DialogPtr settingsDialog, SBSettingsPtr set);
  108. static Boolean LoadSettings(DialogPtr settingsDialog);
  109. static void SaveSettings(DialogPtr settingsDialog);
  110. static Boolean SettingsDialog(SBSettingsPtr settings, LocalRec *theData);
  111. static void CalcSizes(Rect *srcbox, Rect *dstbox, short rows, short columns,
  112.     short spacing, short *hOffset, short *width, short *hSpacing, short *vOffset,
  113.     short *height, short *vSpacing);
  114. static pascal void DrawExample(WindowPtr theWindow, short item);
  115. static pascal void DrawNumber(WindowPtr theWindow, short item);
  116. static pascal void ColumnSliderAction(void);
  117. static pascal void RowSliderAction(void);
  118. static pascal void SpacingSliderAction(void);
  119.  
  120. //========================================================================================
  121. // Export one or more storyboard images
  122. //========================================================================================
  123. pascal short main (short selector, DataExportHandle theData)
  124. {
  125.     Str63                prompt, defaultName;
  126.     FSSpec                curSpec, inSpec;
  127.     LocalRec            localData;
  128.     CGrafPort            localPort;
  129.     StandardFileReply    reply;
  130.     CQDProcs            localProcs;
  131.     QDPutPicUPP            putProc;
  132.     Rect                pageRect, vidBox, destBox;
  133.     PicHandle            thePic;
  134.     GDHandle            oldGD;
  135.     GWorldPtr            theWorld;
  136.     GWorldPtr            oldGW;
  137.     long                writeCount, zero = 0, marker, *marks;
  138.     short                result = 0, i, imageNum, total, count, imagesOnPage, err = 0;
  139.     short                pageNum, hOffset, width, hSpacing, vOffset, height, vSpacing;
  140.     short                clipID, fps, numMarks;
  141.     Boolean                opened = false;
  142.     
  143.     // Act according to the selector
  144.     putProc = NewQDPutPicProc(PutPICTData);
  145.     if (selector == edExecute) {
  146.         GetExportFSSpec(theData, &inSpec);
  147.         PrDebug(0, (char*)"\pFSSpec is %s", inSpec.name);/*DJW*/
  148.         
  149.         // Save the current GWorld/GDevice. Clear localData.
  150.         GetGWorld(&oldGW, &oldGD);
  151.         FillMem((Ptr)&localData, sizeof(LocalRec), 0x00);
  152.         
  153.         // Cache the frame size and initialize a settings record.
  154.         localData.vidRect = (*theData)->bounds;
  155.         InitSettingsRec(&localData.settings);
  156.  
  157.         // Allocate write-ahead buffer and initialize the used offset
  158.         localData.writeBuf = NewPtr(kWriteBufSize);
  159.         if (localData.writeBuf == nil)
  160.             return(1);
  161.         localData.writeUsed = 0;
  162.         
  163.         // Save off the address of localData so that our userItems and put-PICT proc can
  164.         // find them.
  165.         SetAGlobal(gExportRef, (long)&localData);
  166.         
  167.         // Let the user edit the settings
  168.         if (!SettingsDialog(&localData.settings, &localData))
  169.             return(0);
  170.         UpdateAllWindows();
  171.  
  172.         // Put up a standard put file dialog. This gives us a base picture file name.
  173.         GetIndString(prompt, srStrings, ssPICTPrompt);
  174.         GetIndString(defaultName, srStrings, ssPICTDefaultName);
  175.         StandardPutFile(prompt, defaultName, &reply);
  176.         
  177.         // If they didn't cancel the standard put file dialog, export the images.
  178.         if (reply.sfGood) {            
  179.             // Cache up some information and calculate the number of frames per page.
  180.             UpdateAllWindows();
  181.             SpinCurs(10);
  182.             vidBox = (*theData)->bounds;
  183.             SetRect(&pageRect, 0, 0, localData.settings.hSize, localData.settings.vSize);
  184.             total = localData.settings.columns * localData.settings.rows;
  185.             
  186.             // Calculate given the input vidBox, the picture rectangle, and the user's
  187.             // specified rows, columns, and spacing, calculate the scaled size, offsets,
  188.             // and spacing.
  189.             CalcSizes(&vidBox, &pageRect, localData.settings.rows,
  190.                 localData.settings.columns, localData.settings.spacing, &hOffset, &width,
  191.                 &hSpacing, &vOffset, &height, &vSpacing);
  192.  
  193.             // Retrieve the clip ID and calculate an fps value
  194.             clipID = GetExportClipID(theData);
  195.             fps = GetExportFPS(theData);
  196.  
  197.             // Find all of the markers, including unnumbered markers. Markers 0 and 1 are
  198.             // the in- and out-points, respectively. Manually add the out-point to the end
  199.             // of the list (it will always be >= all other markers).
  200.             numMarks = CountClipMarkers(clipID);
  201.             count = 1;
  202.             marks = (long *)NewPtr(numMarks * sizeof(long));
  203.             if (marks == nil)
  204.                 return(1);
  205.             marks[0] = (*theData)->markers[0];
  206.             for (i = 2; i < numMarks; i++) {
  207.                 marker = GetClipMarker(clipID, i, fps);
  208.                 if (marker != -1)
  209.                     marks[count++] = marker;
  210.             }
  211.             marks[count++] = (*theData)->markers[1];
  212.             
  213.             // Now that we know how many images we'll be needing to output, figure out how
  214.             // many pages that'll take. If it's more than one, create an appropriate file
  215.             // name for the first page (if only one page is required, we'll just leave the
  216.             // name as the user typed it).
  217.             imageNum = 0;
  218.             imagesOnPage = 0;
  219.             pageNum = 0;
  220.             if (count > total)
  221.                 FileNameForPage(reply.sfFile.name, curSpec.name, pageNum);
  222.             else BlockMove(reply.sfFile.name, curSpec.name, reply.sfFile.name[0] + 1);
  223.             curSpec.vRefNum = reply.sfFile.vRefNum;
  224.             curSpec.parID = reply.sfFile.parID;
  225.  
  226.             // Make a frame-sized GWorld into which we'll get each frame. If successful,
  227.             // lock it.
  228.             err = SafeNewGWorld(&theWorld, 32, &vidBox, nil, nil, keepLocal);
  229.             if (!err)
  230.                 LockPixels(theWorld->portPixMap);
  231.  
  232.             // Now loop for all images, breaking the list up into pages as we go.
  233.             while (!err && (imageNum < count)) {
  234.             
  235.                 // Delete the destination file, if it exists. Ignore the error. Create
  236.                 // the destination PICT file, then open it.
  237.                 FSpDelete(&curSpec);
  238.                 err = FSpCreate(&curSpec, 'PrMr', 'PICT', reply.sfScript);
  239.                 if (!err) {
  240.                     err = FSpOpenDF(&curSpec, fsWrPerm, &localData.ref);
  241.                     if (err == noErr)
  242.                         opened = true;
  243.                 }
  244.                 
  245.                 if (!err) {
  246.                     // Now open a port into which we'll draw our frame images. Since the
  247.                     // output PICT might be too large to fit in memory (they'll be high-
  248.                     // resolution if scaled down), install a disk-based put-PICT procedure
  249.                     // into our port's putPicProc QD bottleneck.
  250.                     OpenCPort(&localPort);
  251.                     SetPort((GrafPtr)&localPort);
  252.                     ClipRect(&pageRect);
  253.                     SetStdCProcs(&localProcs);
  254.                     localProcs.putPicProc = putProc;
  255.                     localData.thePic = nil;
  256.                     localData.error = 0;
  257.                     localPort.grafProcs = &localProcs;
  258.             
  259.                     // Write the PICT file header (512 bytes of zero).
  260.                     err = WriteZeros(localData.ref, 512);
  261.                     
  262.                     if (!err) {
  263.                         // Write an empty picture header. Later we'll have to re-write
  264.                         // this little section to update the picture size.
  265.                         WriteZeros(localData.ref, sizeof(Picture));
  266.  
  267.                         if (!err) {
  268.                             // Open the picture and stuff the relevant information into
  269.                             // localData so the putPicProc can see it. Set up the initial
  270.                             // picture size to be the size of a Picture structure. The
  271.                             // putPicProc will add to this.
  272.                             thePic = OpenPicture(&pageRect);
  273.                             localData.thePic = thePic;
  274.                             localData.count = sizeof(Picture);
  275.  
  276.                             // Erase then put the images on the page
  277.                             EraseRect(&pageRect);
  278.                             imagesOnPage = 0;
  279.                             while (imagesOnPage < total && imageNum < count) {
  280.                             
  281.                                 // Calculate the location on the page
  282.                                 destBox.left = hOffset +
  283.                                     (imagesOnPage % localData.settings.columns) *
  284.                                     (width + hSpacing);
  285.                                 destBox.top = vOffset +
  286.                                     (imagesOnPage / localData.settings.columns) *
  287.                                     (height + vSpacing);
  288.                                 destBox.right = destBox.left + width;
  289.                                 destBox.bottom = destBox.top + height;
  290.                                 
  291.                                 SetGWorld(theWorld, nil);
  292.                                 EraseRect(&vidBox);
  293.                                 
  294.                                 // Use the Premiere getVideo callback to fill our GWorld
  295.                                 // with the appropriate frame, then put it into our output
  296.                                 // picture with a CopyBits call.
  297.                                 ((*theData)->getVideo)(marks[imageNum], theWorld,
  298.                                     &vidBox, (*theData)->privateData);
  299.                                 SetPort((GrafPtr)&localPort);
  300.                                 CopyBits((BitMap*)&theWorld->portPixMap,
  301.                                     (BitMap*)&localPort.portPixMap,
  302.                                     &theWorld->portRect, &destBox, ditherCopy, nil);
  303.                                 
  304.                                 // Bump the counters of total output images and images
  305.                                 // on this page.
  306.                                 imageNum++;
  307.                                 imagesOnPage++;
  308.                             
  309.                             }
  310.                             
  311.                             // Now we're done with one picture image. Close it.
  312.                             ClosePicture();
  313.                             
  314.                             // Force-flush our writeahead buffer
  315.                             PutPICTData(nil, kFlushBufCmd);
  316.                         }
  317.                         
  318.                         // If an error occurred, store it so our putPicProc will at least
  319.                         // have a chance to TRY to put the picture to rest.
  320.                         if (!err)
  321.                             err = localData.error;
  322.                         
  323.                         // Now that we're done, back up to the picture header and write a
  324.                         // REAL picture header there.
  325.                         if (!err) {
  326.                             SetFPos(localData.ref, fsFromStart, 512);
  327.                             writeCount = sizeof(Picture);
  328.                             err = FSWrite(localData.ref, &writeCount, (Ptr)*thePic);
  329.                         }
  330.                     }
  331.                     
  332.                     // We're done with the port this time around, so close it.
  333.                     CloseCPort(&localPort);
  334.                 }
  335.                 
  336.                 // Close this picture file. Bump fileName by one (this won't matter
  337.                 // if there's only one output file).
  338.                 if (opened)
  339.                     FSClose(localData.ref);
  340.                 if (!err)
  341.                     FileNameForPage(reply.sfFile.name, curSpec.name, ++pageNum);
  342.             }
  343.             DisposePtr((Ptr)marks);
  344.             StopCurs();
  345.         }
  346.         
  347.         // Restore the GWorld/GDevice
  348.         SetGWorld(oldGW, oldGD);
  349.  
  350.         // If there was an error, nuke the last output file.
  351.         if (err) {
  352.             FSpDelete(&curSpec);
  353.             AlertSystem(0, false, srStrings, ssError, 0, 0);
  354.         }
  355.         
  356.         // Restore the GWorld/GDevice
  357.         SetGWorld(oldGW, oldGD);
  358.     }
  359.     
  360.     DisposeRoutineDescriptor(putProc);
  361.     return(result);
  362. }
  363.  
  364. //========================================================================================
  365. // Build a numbered output file name in an internationally cool way. The parameter inName
  366. // is the "base" part of the file name, outName is a pointer to the output buffer, and
  367. // which is the file number.
  368. //========================================================================================
  369. static void FileNameForPage(StringPtr inName, StringPtr outName, short which)
  370. {
  371.     Str255    extStr;
  372.     Handle    extH;
  373.     short    extLen;
  374.     
  375.     // Copy the inName to the outName
  376.     BlockMove(inName, outName, *inName + 1);
  377.  
  378.     // Expand the numbered extension using an expansion template, then copy the results
  379.     // into the Pascal string extStr.
  380.     extH = BuildString(srExtension, nil, which);
  381.     extLen = GetHandleSize(extH);
  382.     if (extLen > 254) extLen = 254;
  383.     BlockMove(*extH, extStr + 1, extLen);
  384.     extStr[0] = extLen;
  385.     DisposeHandle(extH);
  386.     
  387.     // Now that we know how many characters the numbered extension will take, truncate
  388.     // the input name if necessary.
  389.     if (*outName + extLen > 31)
  390.         *outName = 31 - extLen;
  391.     
  392.     // Now put the extension and the input name together
  393.     Append(outName, extStr);
  394. }
  395.  
  396. //========================================================================================
  397. // Write numBytes zeros into the file ref at the current file position.
  398. //========================================================================================
  399. static OSErr WriteZeros(short ref, long numBytes)
  400. {
  401.     long    big = numBytes / sizeof(long);
  402.     long    little = numBytes % sizeof(long);
  403.     long    i, count, zero = 0;
  404.     OSErr    err = noErr;
  405.     
  406.     // Write long-words first
  407.     for (i = 0; i < big && err == noErr; i++) {
  408.         count = sizeof(long);
  409.         err = FSWrite(ref, &count, &zero);
  410.     }
  411.     
  412.     // Now write the rest in bytes
  413.     for (i = 0; i < little && err == noErr; i++) {
  414.         count = sizeof(char);
  415.         err = FSWrite(ref, &count, &zero);
  416.     }
  417.     
  418.     return(err);
  419. }
  420.  
  421. //========================================================================================
  422. // Put a chunk of data into a PICT file (Beaker's buffered version -- 261% faster!)
  423. //========================================================================================
  424. static pascal void PutPICTData(char *buffer, short byteCount)
  425. {
  426.     long        count, remaining;
  427.     LocalRec    *theRec;
  428.     
  429.     // Get a pointer to our local data.
  430.     theRec = (LocalRec *)GetAGlobal(gExportRef);
  431.     
  432.     // If we get a magic call with byteCount equal to kFlushBufCmd (-1), we know that
  433.     // the main loop is trying to flush the writeahead buffer. Special-case this.
  434.     if (byteCount == kFlushBufCmd) {
  435.         if (theRec->writeUsed > 0) {
  436.             count = theRec->writeUsed;
  437.             theRec->error = FSWrite(theRec->ref, &count, theRec->writeBuf);
  438.             theRec->writeUsed = 0;
  439.         }
  440.         return;
  441.     }
  442.     
  443.     // If an error hasn't occurred, write the bytes QuickDraw is handing us to the output
  444.     // file referenced in our local data. Each time we write out some data, bump the byte-
  445.     // count so we know exactly how big the picture is.
  446.     if (!theRec->error) {
  447.         theRec->count += byteCount;
  448.         remaining = kWriteBufSize - theRec->writeUsed;
  449.         
  450.         // For big writes, flush buffered data then write the input data in one chunk.
  451.         if (byteCount > kWriteBufSize) {
  452.             // Write whatever's in the buffer
  453.             if (theRec->writeUsed > 0) {
  454.                 count = theRec->writeUsed;
  455.                 theRec->error = FSWrite(theRec->ref, &count, theRec->writeBuf);
  456.                 theRec->writeUsed = 0;
  457.             }
  458.             
  459.             // Loop write the input data in one big chunk
  460.             if (theRec->error == noErr) {
  461.                 count = byteCount;
  462.                 theRec->error = FSWrite(theRec->ref, &count, buffer);
  463.             }
  464.         } else {
  465.             // If the write overflows our buffer, copy the data into our buffer, flush it,
  466.             // then buffer the remaining input data.
  467.             if (byteCount > remaining) {
  468.                 BlockMove(buffer, theRec->writeBuf + theRec->writeUsed, remaining);
  469.                 count = kWriteBufSize;
  470.                 theRec->error = FSWrite(theRec->ref, &count, theRec->writeBuf);
  471.                 buffer += remaining;
  472.                 byteCount -= remaining;
  473.  
  474.                 // Buffer the remaining input bytes
  475.                 if (byteCount > 0) {
  476.                     BlockMove(buffer, theRec->writeBuf, byteCount);
  477.                     theRec->writeUsed = byteCount;
  478.                 } else theRec->writeUsed = 0;
  479.             } else {
  480.                 // This write fits in the buffer, so just buffer it.
  481.                 BlockMove(buffer, theRec->writeBuf + theRec->writeUsed, byteCount);
  482.                 theRec->writeUsed += byteCount;
  483.             }
  484.         }
  485.                     
  486.         // Keep the running count in the picture header up to date, too.
  487.         if (theRec->thePic)
  488.             (*theRec->thePic)->picSize = theRec->count & 0x7FFF;
  489.     }
  490. }
  491.  
  492. //========================================================================================
  493. // Initialize an SBSettingsRec with default values
  494. //========================================================================================
  495. static void InitSettingsRec(SBSettingsPtr settings)
  496. {
  497.     settings->hSize = 576;            // 8.00 inches (LW letter)
  498.     settings->vSize = 776;            // 10.78 inches (LW letter)
  499.     settings->rows = 2;
  500.     settings->columns = 2;
  501.     settings->spacing = 10;
  502. }
  503.  
  504. //========================================================================================
  505. // Shove the values in set into settingsDialog.
  506. //========================================================================================
  507. static void SetToDialog(DialogPtr settingsDialog, SBSettingsPtr set, Boolean redraw)
  508. {
  509.     SetIVal(settingsDialog, iHorizSize, set->hSize);
  510.     SetIVal(settingsDialog, iVertSize, set->vSize);
  511.     SetCValue(settingsDialog, iRowSlider, set->rows);
  512.     SetCValue(settingsDialog, iColumnSlider, set->columns);
  513.     SetCValue(settingsDialog, iSpacingSlider, set->spacing);
  514.     
  515.     if (redraw) {
  516.         DrawNumber(settingsDialog, iRowNumber);
  517.         DrawNumber(settingsDialog, iColumnNumber);
  518.         DrawNumber(settingsDialog, iSpacingNumber);
  519.         DrawExample(settingsDialog, iExample);
  520.     }
  521. }
  522.  
  523. //========================================================================================
  524. // Grab the values in settingsDialog into set.
  525. //========================================================================================
  526. static void DialogToSet(DialogPtr settingsDialog, SBSettingsPtr set)
  527. {
  528.     set->hSize = GetIVal(settingsDialog, iHorizSize);
  529.     set->vSize = GetIVal(settingsDialog, iVertSize);
  530.     set->rows = GetCValue(settingsDialog, iRowSlider);
  531.     set->columns = GetCValue(settingsDialog, iColumnSlider);
  532.     set->spacing = GetCValue(settingsDialog, iSpacingSlider);
  533. }
  534.  
  535. //========================================================================================
  536. // Load settings from a file
  537. //========================================================================================
  538. static Boolean LoadSettings(DialogPtr settingsDialog)
  539. {
  540.     StandardFileReply    reply;
  541.     SFTypeList            types;
  542.     SBSettingsRec        loadSet;
  543.     GrafPtr                oldPort;
  544.     long                count;
  545.     OSErr                err = noErr;
  546.     short                setRef;
  547.     Boolean                opened = false;
  548.     
  549.     GetPort(&oldPort);
  550.  
  551.     types[0] = kSBSetType;
  552.     StandardGetFile(nil, 1, types, &reply);
  553.     
  554.     if (reply.sfGood) {
  555.         err = FSpOpenDF(&reply.sfFile, fsWrPerm, &setRef);
  556.         if (err == noErr)
  557.             opened = true;
  558.         if (err == noErr) {
  559.             count = sizeof(SBSettingsRec);
  560.             err = FSRead(setRef, &count, (Ptr)&loadSet);
  561.         }
  562.         if (opened)
  563.             FSClose(setRef);
  564.     }
  565.     
  566.     SetPort(oldPort);
  567.     if (reply.sfGood && err == noErr) {
  568.         SetToDialog(settingsDialog, &loadSet, kRedraw);
  569.         SelIText(settingsDialog, iHorizSize, 0, 32000);
  570.         return(true);
  571.     } else return(false);
  572. }
  573.  
  574. //========================================================================================
  575. // Save settings to a file
  576. //========================================================================================
  577. static void SaveSettings(DialogPtr settingsDialog)
  578. {
  579.     Str63                prompt, defaultName;
  580.     StandardFileReply    reply;
  581.     SBSettingsRec        curSet;
  582.     GrafPtr                oldPort;
  583.     long                count;
  584.     OSErr                err = noErr;
  585.     short                setRef;
  586.     Boolean                opened = false;
  587.     
  588.     DialogToSet(settingsDialog, &curSet);
  589.     GetPort(&oldPort);
  590.  
  591.     GetIndString(prompt, srStrings, ssSetPrompt);
  592.     GetIndString(defaultName, srStrings, ssSetDefaultName);
  593.     StandardPutFile(prompt, defaultName, &reply);
  594.     
  595.     if (reply.sfGood) {
  596.         FSpDelete(&reply.sfFile);
  597.         err = FSpCreate(&reply.sfFile, 'PrMr', kSBSetType, smSystemScript);
  598.         if (err == noErr) {
  599.             err = FSpOpenDF(&reply.sfFile, fsWrPerm, &setRef);
  600.             if (err == noErr)
  601.                 opened = true;
  602.         }
  603.         if (err == noErr) {
  604.             count = sizeof(SBSettingsRec);
  605.             err = FSWrite(setRef, &count, (Ptr)&curSet);
  606.         }
  607.         if (opened)
  608.             FSClose(setRef);
  609.     }
  610.     
  611.     SetPort(oldPort);
  612. }
  613.  
  614. //========================================================================================
  615. // Conduct the settings dialog. Store the result into settings. Returns true if the user
  616. // ended the dialog with OK, false if he Cancelled.
  617. //========================================================================================
  618. static Boolean SettingsDialog(SBSettingsPtr settings, LocalRec *theData)
  619. {
  620.     Rect        offBox;
  621.     DialogPtr    settingsDialog;
  622.     GrafPtr        oldPort;
  623.     short        item;
  624.     Boolean        done = false;
  625.     
  626.     // Get and position the dialog
  627.     GetPort(&oldPort);
  628.     settingsDialog = MyGetNewDialog(srDialog, nil, (WindowPtr)-1);
  629.     CenterWindowOnMain(settingsDialog);
  630.     SetPort(settingsDialog);
  631.     
  632.     // Set up item values and userItems
  633.     SetToDialog(settingsDialog, settings, kNoRedraw);
  634.     SetCAction(settingsDialog, iRowSlider, (ProcPtr)RowSliderAction);
  635.     SetCAction(settingsDialog, iColumnSlider, (ProcPtr)ColumnSliderAction);
  636.     SetCAction(settingsDialog, iSpacingSlider, (ProcPtr)SpacingSliderAction);
  637.     UserItem(settingsDialog, iRowNumber, (UserItemProcPtr)DrawNumber);
  638.     UserItem(settingsDialog, iColumnNumber, (UserItemProcPtr)DrawNumber);
  639.     UserItem(settingsDialog, iSpacingNumber, (UserItemProcPtr)DrawNumber);
  640.     UserItem(settingsDialog, iExample, (UserItemProcPtr)DrawExample);
  641.     UserItem(settingsDialog, iImageSizeGroup, (UserItemProcPtr)DrawItemFrame);
  642.     SelIText(settingsDialog, iHorizSize, 0, 32000);
  643.     
  644.     // Try to make an offscreen buffer
  645.     GetDRect(settingsDialog, iExample, &offBox);
  646.     if (SafeNewGWorld(&theData->offWorld, 1, &offBox, nil, nil, keepLocal))
  647.         LockPixels(theData->offWorld->portPixMap);
  648.     
  649.     // Conduct the dialog
  650.     ShowModal(settingsDialog);
  651.     do {
  652.         PrModalDialog((ModalFilterProcPtr)notextfilter, &item);
  653.         switch (item) {
  654.             case iOK:
  655.                 if (!Validate(settingsDialog, iHorizSize, kMinHSize, kMaxHSize) &&
  656.                         !Validate(settingsDialog, iVertSize, kMinVSize, kMaxVSize)) {
  657.                     DialogToSet(settingsDialog, settings);
  658.                     done = true;
  659.                 } else DrawExample(settingsDialog, iExample);
  660.                 break;
  661.             case iCancel:
  662.                 done = true;
  663.                 break;
  664.             case iLoad:
  665.                 LoadSettings(settingsDialog);
  666.                 break;
  667.             case iSave:
  668.                 SaveSettings(settingsDialog);
  669.                 break;
  670.             case iHorizSize:
  671.             case iVertSize:
  672.                 DrawExample(settingsDialog, iExample);
  673.                 break;
  674.             default:
  675.                 break;
  676.         }
  677.     } while (!done);
  678.     DisposeModal(settingsDialog);
  679.     
  680.     // Lose the offworld if we made one
  681.     if (theData->offWorld != nil) {
  682.         DisposeGWorld(theData->offWorld);
  683.         theData->offWorld = nil;
  684.     }
  685.     
  686.     // Restore the port and return true if the user exited with OK.
  687.     SetPort(oldPort);
  688.     return(item == iOK);
  689. }
  690.  
  691. //========================================================================================
  692. // Calculate the parameters for the drawing boxes
  693. //========================================================================================
  694. static void CalcSizes(Rect *srcbox, Rect *dstbox, short rows, short columns,
  695.     short spacing, short *hOffset, short *width, short *hSpacing, short *vOffset,
  696.     short *height, short *vSpacing)
  697. {
  698.     long    hRatio, vRatio, theRatio;
  699.     short    srcWidth, srcHeight, dstWidth, dstHeight;
  700.     short    usefulH, usefulV;
  701.     
  702.     // Calculate dimensions of the source and destination rectangles.
  703.     srcWidth = srcbox->right - srcbox->left;
  704.     srcHeight = srcbox->bottom - srcbox->top;
  705.     dstWidth = dstbox->right - dstbox->left;
  706.     dstHeight = dstbox->bottom - dstbox->top;
  707.     
  708.     // Store horizontal and vertical spacing
  709.     *hSpacing = srcWidth * spacing / 100;
  710.     *vSpacing = srcHeight * spacing / 100;
  711.     
  712.     // Calculate the horizontal scale-down factor (hRatio, which is a 16.16 fixed-point
  713.     // number).
  714.     usefulH = (dstWidth - (columns + 1) * (*hSpacing)) / columns;
  715.     if (usefulH > srcWidth)
  716.         hRatio = 0x00010000;
  717.     else hRatio = (usefulH << 16) / srcWidth;
  718.     
  719.     // Calculate the vertical scale-down factor (vRatio, which is a 16.16 fixed-point
  720.     // number).
  721.     usefulV = (dstHeight - (rows+1) * (*vSpacing)) / rows;
  722.     if (usefulV > srcHeight)
  723.         vRatio = 0x00010000;
  724.     else vRatio = (usefulV << 16) / srcHeight;
  725.     
  726.     // Use the smaller of hRatio or vRatio.
  727.     if (hRatio < vRatio)
  728.         theRatio = hRatio;
  729.     else theRatio = vRatio;
  730.     
  731.     // Calculate and store scaled width and height.
  732.     *width = (srcWidth * theRatio) >> 16;
  733.     *height = (srcHeight * theRatio) >> 16;
  734.  
  735.     // Calculate and store horizontal and vertical offsets.
  736.     *hOffset = (dstWidth - columns * (*width) - (columns - 1) * (*hSpacing)) / 2;
  737.     *vOffset = (dstHeight - rows * (*height) - (rows - 1) * (*vSpacing)) / 2;
  738. }
  739.  
  740. //========================================================================================
  741. // Draw the example box
  742. //========================================================================================
  743. static pascal void DrawExample(WindowPtr theWindow, short item)
  744. {
  745.     SBSettingsRec    set;
  746.     Rect            box, hTemp, vTemp, drawBox, srcBox, dstBox, newBox;
  747.     GDHandle        gd;
  748.     GWorldPtr        gw;
  749.     LocalRec        *theData;
  750.     short            width, height, h, v;
  751.     short            hOffset, vOffset, hSpacing, vSpacing;
  752.     long            ratio;
  753.     
  754.     // Get a pointer to our local data
  755.     theData = (LocalRec *)GetAGlobal(gExportRef);
  756.  
  757.     // If there's an offworld, use it
  758.     GetGWorld(&gw, &gd);
  759.     if (theData->offWorld != nil)
  760.         SetGWorld(theData->offWorld, nil);
  761.     
  762.     PenNormal(); ForeColor(blackColor);
  763.     GetDRect(theWindow, item, &box);
  764.     EraseRect(&box);
  765.  
  766.     // Retrieve the settings from the three sliders
  767.     DialogToSet(theWindow, &set);
  768.     if (set.hSize < kMinHSize)            // Pin sizes even if they're bogus
  769.         set.hSize = kMinHSize;
  770.     if (set.hSize > kMaxHSize)
  771.         set.hSize = kMaxHSize;
  772.     if (set.vSize < kMinVSize)
  773.         set.vSize = kMinVSize;
  774.     if (set.vSize > kMaxVSize)
  775.         set.vSize = kMaxVSize;
  776.     
  777.     // Get the source (video) and destination (printer page) rectangles.
  778.     srcBox = theData->vidRect;
  779.     SetRect(&dstBox, 0, 0, set.hSize, set.vSize);
  780.  
  781.     // Call CalcSizes to calculate all the values.
  782.     CalcSizes(&srcBox, &dstBox, set.rows, set.columns, set.spacing, &hOffset, &width,
  783.         &hSpacing, &vOffset, &height, &vSpacing);
  784.     
  785.     // Calculate the printer page aspect ratio and calculate an appropriate display rect.
  786.     ratio = ((dstBox.right - dstBox.left) << 16) / (dstBox.bottom - dstBox.top);
  787.     newBox = box;
  788.     if (ratio >= 0x00010000) {
  789.         // Landscape orientation
  790.         newBox.bottom = box.top + ((box.bottom - box.top) << 16) / ratio;
  791.         OffsetRect(&newBox, 0, (box.bottom - newBox.bottom) / 2);
  792.     } else {
  793.         // Portrait orientation
  794.         newBox.right = box.left + (((box.bottom - box.top) * ratio) >> 16);
  795.         OffsetRect(&newBox, (box.right - newBox.right) / 2, 0);
  796.     }
  797.     FrameRect(&newBox);
  798.     InsetRect(&newBox, 1, 1);
  799.     
  800.     // Draw the frame images in 50% gray
  801.     SetRect(&vTemp, dstBox.left + hOffset, dstBox.top + vOffset,
  802.         dstBox.left + width + hOffset, dstBox.top + height + vOffset);
  803.     PenPat(*GetPattern(srGray));
  804.     SetGray(0x7fff);
  805.     for (v = 0; v < set.rows; v++) {
  806.         hTemp = vTemp;
  807.         for (h = 0; h < set.columns; h++) {
  808.             drawBox = hTemp;
  809.             MapRect(&drawBox, &dstBox, &newBox);
  810.             FrameRect(&drawBox);
  811.             OffsetRect(&hTemp, hSpacing + width, 0);
  812.         }
  813.         OffsetRect(&vTemp, 0, vSpacing + height);
  814.     }
  815.     ForeColor(blackColor); PenNormal();
  816.     
  817.     // Blit back if we're drawing offscreen
  818.     if (theData->offWorld != nil) {
  819.         SetGWorld(gw, gd);
  820.         CopyBits((BitMap *)&theData->offWorld->portPixMap, &theWindow->portBits,
  821.             &box, &box, srcCopy, nil);
  822.     }
  823.     
  824.     SetGWorld(gw, gd);
  825. }
  826.  
  827. //========================================================================================
  828. // Draw the value of the control at item - 1 in item's rectangle.
  829. //========================================================================================
  830. static pascal void DrawNumber(WindowPtr theWindow, short item)
  831. {
  832.     Str32        str;
  833.     Rect        box;
  834.     FontInfo    info;
  835.     LocalRec    *theData;
  836.     short        value;
  837.     
  838.     // Get a pointer to our local data
  839.     theData = (LocalRec *)GetAGlobal(gExportRef);
  840.  
  841.     GetDRect(theWindow, item, &box);
  842.     value = GetCValue(theWindow, item - 1);
  843.     NumToString(value, str);
  844.     EraseRect(&box);
  845.     SetFont(fontGeneva9);
  846.     GetFontInfo(&info);
  847.     MoveTo(box.left, box.bottom - info.descent);
  848.     DrawString(str);
  849.     SetFont(fontChicago12);
  850. }
  851.  
  852. //========================================================================================
  853. // action procedure for the column slider
  854. //========================================================================================
  855. static pascal void ColumnSliderAction(void)
  856. {
  857.     GrafPtr    thePort;
  858.     
  859.     GetPort(&thePort);
  860.     DrawNumber(thePort, iColumnNumber);
  861.     DrawExample(thePort, iExample);
  862. }
  863.  
  864. //========================================================================================
  865. // action procedure for the row slider
  866. //========================================================================================
  867. static pascal void RowSliderAction(void)
  868. {
  869.     GrafPtr    thePort;
  870.     
  871.     GetPort(&thePort);
  872.     DrawNumber(thePort, iRowNumber);
  873.     DrawExample(thePort, iExample);
  874. }
  875.  
  876. //========================================================================================
  877. // action procedure for the spacing slider
  878. //========================================================================================
  879. static pascal void SpacingSliderAction(void)
  880. {
  881.     GrafPtr    thePort;
  882.     
  883.     GetPort(&thePort);
  884.     DrawNumber(thePort, iSpacingNumber);
  885.     DrawExample(thePort, iExample);
  886. }
  887.